package top.haijunit.common.redis.interceptor;

import jakarta.validation.constraints.NotNull;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.springframework.context.expression.MethodBasedEvaluationContext;
import org.springframework.core.DefaultParameterNameDiscoverer;
import org.springframework.core.ParameterNameDiscoverer;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import top.haijunit.common.redis.RedissonService;
import top.haijunit.common.redis.annotation.RedissonLock;
import top.haijunit.common.redis.domain.RedissonLockWrapper;

import java.lang.reflect.Method;
import java.util.Objects;

/**
 * redisson锁拦截器
 *
 * @author zhanghaijun
 * @since 2024-07-31
 */
public class RedissonLockInterceptor implements MethodInterceptor {
    private final RedissonService redissonService;

    private final ParameterNameDiscoverer parameterNameDiscoverer = new DefaultParameterNameDiscoverer();

    private final SpelExpressionParser spelExpressionParser = new SpelExpressionParser();

    public RedissonLockInterceptor(RedissonService redissonService) {
        this.redissonService = redissonService;
    }

    @Override
    public Object invoke(MethodInvocation invocation) throws Throwable {
        RedissonLockWrapper lockConfigBO = getRedissonLockWrapper(invocation);
        return redissonService.execute(lockConfigBO, () -> {
            try {
                return invocation.proceed();
            } catch (Exception e) {
                throw e;
            } catch (Throwable e) {
                throw new RuntimeException(e);
            }
        });
    }

    private RedissonLockWrapper getRedissonLockWrapper(MethodInvocation invocation) {
        Method method = invocation.getMethod();
        RedissonLock annotation = method.getAnnotation(RedissonLock.class);
        RedissonLockWrapper lockConfigBO = new RedissonLockWrapper();
        lockConfigBO.setLockKey(parseLockKey(invocation, annotation.lockKey()));
        lockConfigBO.setWaitTime(annotation.waitTime());
        lockConfigBO.setLeaseTime(annotation.leaseTime());
        return lockConfigBO;
    }

    private String parseLockKey(MethodInvocation invocation, String expression) {
        EvaluationContext evaluationContext = new MethodBasedEvaluationContext(Objects.requireNonNull(invocation.getThis()), invocation.getMethod(), invocation.getArguments(), parameterNameDiscoverer);
        return (String) spelExpressionParser.parseExpression(expression).getValue(evaluationContext);
    }
}
